#include "umainwindow.h"
#include "utitlebar.h"
#include "ustylebutton.h"
#include "uapplication.h"
#include "style/upalette.h"
#include "style/uproxystyle.h"
#include "uframelesshelper.h"

#ifdef Q_OS_WIN
#include <windowsx.h>
#include <dwmapi.h>
#pragma comment (lib, "Dwmapi.lib")
#pragma comment (lib, "User32.lib")
#endif

#include <QStylePainter>
#include <QVBoxLayout>
#include <QMouseEvent>
#include <QApplication>
#include <QPainterPath>
#include <QScreen>
#include <QWindow>
#include <QMenu>

class UMainWindowPrivate
{
public:
    UMainWindowPrivate(UMainWindow* q) : q_ptr(q) {}
    ~UMainWindowPrivate() {};

private:
    Q_DECLARE_PUBLIC(UMainWindow)
    Q_DISABLE_COPY(UMainWindowPrivate)
    UMainWindow* const q_ptr = nullptr;

    bool m_isOpen = false;
    int m_radius = 8;
    int m_role = -1;
    int m_margins = 8;

    UTitleBar* m_titleBar = Q_NULLPTR;
    UFramelessHelper *m_pFramelessHelper = Q_NULLPTR;
};


UMainWindow::UMainWindow(QWidget* parent) : QMainWindow(parent)
    , d_ptr(new UMainWindowPrivate(this))
{
    setObjectName("UMainWindow");
    setWindowFlags(Qt::FramelessWindowHint | windowFlags());
    setAttribute(Qt::WA_TranslucentBackground);
    setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
    setMouseTracking(true);
    setContentsMargins(d_ptr->m_margins, d_ptr->m_margins, d_ptr->m_margins, d_ptr->m_margins);
    addDropShadowEffect(true);

    d_ptr->m_titleBar = new UTitleBar(this);
    installEventFilter(d_ptr->m_titleBar);
    QMainWindow::setMenuWidget(d_ptr->m_titleBar);
    setWindowTitle(qApp->applicationName());
    setWindowIcon(qApp->windowIcon());
    initConnections();
    initWindowLong();

#ifndef Q_OS_WIN
    d_ptr->m_pFramelessHelper = new UFramelessHelper(this);
    d_ptr->m_pFramelessHelper->setDragWidget(this);
#endif
}

UMainWindow::~UMainWindow()
{
    Q_D(UMainWindow);
    if (d->m_pFramelessHelper)
        d->m_pFramelessHelper->removeDragWidget();
}

void UMainWindow::setMargins(int nMargins)
{
    Q_D(UMainWindow);
    if (d->m_margins != nMargins)
    {
        d->m_margins = nMargins;
        setContentsMargins(nMargins, nMargins, nMargins, nMargins);
    }
}

void UMainWindow::initConnections()
{
    Q_D(UMainWindow);
    connect(d->m_titleBar, &UTitleBar::windowStateChange, this, &UMainWindow::onWindowStateChange);
    connect(d->m_titleBar, &UTitleBar::minimizeButtonClicked, this, &UMainWindow::onMinimizeButtonClicked);
    connect(d->m_titleBar, &UTitleBar::maximizeButtonClicked, this, &UMainWindow::onMaximizeButtonClicked);
    connect(d->m_titleBar, &UTitleBar::closeButtonClicked, this, &UMainWindow::onCloseButtonClicked);
}

void UMainWindow::addMenu(QMenu* menu)
{
    Q_D(UMainWindow);
    d->m_titleBar->setMenu(menu);
}

void UMainWindow::addDropShadowEffect(bool isOpen)
{
    Q_D(UMainWindow);
    if (d->m_isOpen != isOpen)
    {
        d->m_isOpen = isOpen;
        update();
    }
}

void UMainWindow::setRadius(int radius)
{
    Q_D(UMainWindow);
    if (d->m_radius != radius)
    {
        d->m_radius = radius;
        update();
    }
}

void UMainWindow::setBackground(int role)
{
    Q_D(UMainWindow);
    if (d->m_role != role)
    {
        d->m_role = role;
        update();
    }
}

QRect UMainWindow::screenRect()
{
    return QApplication::primaryScreen()->geometry();
}

void UMainWindow::paintEvent(QPaintEvent* event)
{
    Q_D(UMainWindow);
    QPainter painter(this);
    QColor backgroundColor = (d->m_role >= 0) ? uApp->applicationPalette()->color(d->m_role) : palette().color(backgroundRole());

    int radius = this->isMaximized() ? 0 : d->m_radius;
    QPainterPath path;
    path.addRoundedRect(rect(), radius, radius);
    painter.setClipPath(path);

    int margins = contentsMargins().left();
    QRect rect = QRect(margins, margins, width() - margins * 2, height() - margins * 2);
    if (d->m_isOpen && margins > 0)
    {
        UProxyStyle::drawDropShadow(painter, this->rect(), margins, radius, uApp->applicationPalette()->color(QPalette::Shadow));
    }

    QPainterPath rectPath;
    rectPath.addRoundedRect(rect, radius, radius);
    painter.fillPath(rectPath, backgroundColor);
}

void UMainWindow::initWindowLong()
{
#ifdef Q_OS_WIN
    HWND hwnd = HWND(winId());
    DWORD style = ::GetWindowLong(hwnd, GWL_STYLE);
    style &= ~WS_SYSMENU;
    ::SetWindowLong(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CAPTION);
    auto window = this->windowHandle();
    connect(window, &QWindow::screenChanged, this, [ = ]
    {
        auto hWnd = reinterpret_cast<HWND>(window->winId());
        SetWindowPos(hWnd, nullptr, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
    });
#endif
}

void UMainWindow::onWindowStateChange()
{
    Q_D(UMainWindow);
    UStyleButton* closeButton = d->m_titleBar->findChild<UStyleButton*>("closeButton");

    if (this->isMaximized())
    {
        addDropShadowEffect(false);
        d->m_titleBar->setRadius(0);
        if (closeButton)
            closeButton->setRadius(0);
    }
    else
    {
        addDropShadowEffect(true);
        d->m_titleBar->setRadius(d->m_radius);
        if (closeButton)
            closeButton->setRadius(d->m_radius);
    }
#ifndef Q_OS_WIN
    int margins = this->isMaximized() ? 0 : d->margins;
    setContentsMargins(margins, margins, margins, margins);
#endif
}

void UMainWindow::onMinimizeButtonClicked()
{
    showMinimized();
}

void UMainWindow::onMaximizeButtonClicked()
{
    isMaximized() ? showNormal() : showMaximized();
}

void UMainWindow::onCloseButtonClicked()
{
    close();
}

void UMainWindow::setTitleBarMoveable(bool bMoveable)
{
    Q_D(UMainWindow);
    d->m_titleBar->setMoveable(bMoveable);
}

UTitleBar* UMainWindow::titleBar() const
{
    Q_D(const UMainWindow);
    return d->m_titleBar;
}

void UMainWindow::moveToCenter()
{
    move(QApplication::primaryScreen()->geometry().center() - rect().center());
}

void UMainWindow::moveToCenter(const QPoint &point)
{
    move(QApplication::screenAt(point)->geometry().center() - rect().center());
}

#ifdef Q_OS_WIN
bool UMainWindow::nativeEvent(const QByteArray &eventType, void* message, long* result)
{
    Q_D(UMainWindow);
    MSG* msg = (MSG*)message;

    switch (msg->message)
    {
        case WM_NCCALCSIZE: // remove titlebar
        {
            *result = WVR_REDRAW;
            return true;
        }
        case WM_NCHITTEST:  // hittest
        {
            int margins = contentsMargins().left();
            const LONG borderWidth = margins;
            const LONG cornerWidth = margins + 8;
            RECT winrect;
            GetWindowRect(HWND(effectiveWinId()), &winrect);
            long x = GET_X_LPARAM(msg->lParam);
            long y = GET_Y_LPARAM(msg->lParam);

            bool resizeWidth = minimumWidth() != maximumWidth();
            bool resizeHeight = minimumHeight() != maximumHeight();

            if (resizeWidth)
            {
                //left border
                if (x >= winrect.left && x < winrect.left + borderWidth)
                {
                    *result = HTLEFT;
                }
                //right border
                if (x < winrect.right && x >= winrect.right - borderWidth)
                {
                    *result = HTRIGHT;
                }
            }
            if (resizeHeight)
            {
                //bottom border
                if (y < winrect.bottom && y >= winrect.bottom - borderWidth)
                {
                    *result = HTBOTTOM;
                }
                //top border
                else if (y >= winrect.top && y < winrect.top + borderWidth)
                {
                    *result = HTTOP;
                }
            }
            if (resizeWidth && resizeHeight)
            {
                //bottom left corner
                if (x >= winrect.left && x < winrect.left + cornerWidth &&
                    y < winrect.bottom && y >= winrect.bottom - cornerWidth)
                {
                    *result = HTBOTTOMLEFT;
                }
                //bottom right corner
                else if (x < winrect.right && x >= winrect.right - cornerWidth &&
                         y < winrect.bottom && y >= winrect.bottom - cornerWidth)
                {
                    *result = HTBOTTOMRIGHT;
                }
                //top left corner
                else if (x >= winrect.left && x < winrect.left + cornerWidth &&
                         y >= winrect.top && y < winrect.top + cornerWidth)
                {
                    *result = HTTOPLEFT;
                }
                //top right corner
                else if (x < winrect.right && x >= winrect.right - cornerWidth &&
                         y >= winrect.top && y < winrect.top + cornerWidth)
                {
                    *result = HTTOPRIGHT;
                }
            }
            if (0 != *result)
            {
                return true;
            }
            if (!d->m_titleBar)
            {
                return false;
            }

            // support highdpi
            double dpr = this->devicePixelRatioF();
            QPoint pos = d->m_titleBar->mapFromGlobal(QPoint(x / dpr, y / dpr));

            if (!d->m_titleBar->rect().contains(pos))
            {
                return false;
            }
            QWidget* child = d->m_titleBar->childAt(pos);
            if (!child)
            {
                *result = HTCAPTION;
                return true;
            }
            return false;
        }
        default:
            break;
    }

    return QMainWindow::nativeEvent(eventType, message, result);
}
#endif
